#include "inc/hw_memmap.h"
#include "usci_uart.h"
#include "ucs.h"
#include "wdt_a.h"
#include "gpio.h"
#include "usci_i2c.h"
#include "adc12_a.h"

#include "h1010.h"
#include "ConfigMem.h"
#include "SI.h"
#include "SOSZ.h"
#include "I2C_IO_Driver.h"
#include "Relay.h"
#include "Fault.h"
#include "Serial.h"
#include "Test.h"
#include "main.h"

#include <string.h>

enum SerialState {
	ST_INIT = 0,
	ST_ACK_FROM_HI_BYTE,
	ST_ACK_FROM_LO_BYTE,
	ST_ACK_FROM_REV_BYTE,
	ST_RCV_CONFIG_1,
	ST_RCV_CONFIG_2,
	ST_RCV_CONFIG_3,
	ST_RCV_CONFIG,
	ST_RCV_CMP
};
enum SerialMode {
	MODE_SEND_REV_TO_PC = 0x00,
	MODE_RCV_CFG_FROM_PC = 0x01,
	MODE_SEND_CFG_TO_PC = 0x02
};

#define BAUD_RATE_9600 9600
#define BAUD_RATE_19200 19200
#define BAUD_RATE_57600 57600
#define BAUD_RATE_115200 115200

#define BAUD_RATE                  BAUD_RATE_19200
#define UPPER_REV_BYTE_COUNT 0x00
#define LOWER_REV_BYTE_COUNT 0x01
#define REV_BYTE 0x86//0x83//0x86//0x81//0x00

//extern unsigned char * pImage;
//unsigned char Image[5029];
unsigned char header[8];
unsigned char * pHeader = &header[0];
unsigned char Serial_serialOperation = 0;

//struct SysConfigAddress stSysConfigAddress;
//struct SysConfig stSysConfig;
//struct SI stSI[TOTAL_SI];
//SO stSZ[TOTAL_SZ];
//SO stSO[TOTAL_SO];
//SO stREL[TOTAL_REL];
//GA stGA;
//xAbort stAbort;
//struct HWTests stHWTests;
//FWS stFWS[TOTAL_FWS_SECTORS];
//struct REVNO stREVNO;
//FWS_Ext02 stFWS02[TOTAL_FWS_SECTORS];
//DSP_Message stMESSAGE[TOTAL_MESSAGE];

unsigned char receivedData = 0x00;
//#define buffSize 5300
unsigned int buffSize = 0;
//static unsigned char buff[buffSize];
//static unsigned char * pBuff;//= &buff[0];
static unsigned char * p_buffBegin = 0;//= &buff[0];
static unsigned int count = 0;
static unsigned int in = 0;
static unsigned int out = 0;
//static unsigned int ind = 0;
static unsigned char processData = 0;
//static char buff[5300];
//static char * p = &buff[0];
static int turn = 0;
static BYTE Serial_mode = MODE_SEND_REV_TO_PC;

BYTE lastByte = 0;
WORD byteIndex = 0;

void upload_config (void);
void download_config (void); // what I think is right
void get_config_rev(void);


//#define ST_INIT 0
//#define ST_ACK_FROM_HI_BYTE 1
//#define ST_ACK_FROM_LO_BYTE 2
//#define ST_ACK_FROM_REV_BYTE 3
//#define ST_RCV_CONFIG_1 4
//#define ST_RCV_CONFIG_2 5
//#define ST_RCV_CONFIG_3 6
//#define ST_RCV_CONFIG 7
int Serial_init(unsigned char * pBuffBegin, unsigned int _buffSize)
{
	if (pBuffBegin == 0)
		return -1;
	p_buffBegin = pBuffBegin;
	if (p_buffBegin == 0)
		return -2;
	buffSize = _buffSize;
//	pBuff = p_buffBegin;

    //P3.4,5 = USCI_A0 TXD/RXD
    GPIO_setAsPeripheralModuleFunctionInputPin(
        GPIO_PORT_P3,
        GPIO_PIN4 + GPIO_PIN5
        );

    //Initialize USCI UART module
    if ( STATUS_FAIL == USCI_UART_init(USCI_A0_BASE,
             USCI_UART_CLOCKSOURCE_SMCLK,
             UCS_getSMCLK(UCS_BASE),
             BAUD_RATE,
             USCI_UART_NO_PARITY,
             USCI_UART_LSB_FIRST,
             USCI_UART_ONE_STOP_BIT,
             USCI_UART_MODE,
             USCI_UART_OVERSAMPLING_BAUDRATE_GENERATION
             )){
        return -3;
    }

    //Enable UART module for operation
    USCI_UART_enable(USCI_A0_BASE);

    //Enable Receive Interrupt
    USCI_UART_enableInterrupt(USCI_A0_BASE, USCI_UART_RECEIVE_INTERRUPT);
//    int i;
//    for (i=0;i<buffSize;i++) p_buffBegin[i] = 0x55;
    memset(p_buffBegin, 0x55, buffSize);
    Serial_mode = MODE_SEND_REV_TO_PC;

    return 0;

//    configMem_updateFLASH(&buff[8], 5029);


    //Enter LPM3, interrupts enabled
//    __bis_SR_register(LPM3_bits + GIE);

}

void disableAllMaskableInterruptsButSerial(void)
{
	SI_disable25msISR();
	USCI_I2C_disable(USCI_B1_BASE);
}
void enableAllMaskableInterruptsButSerial(void)
{
	SI_enable25msISR();
	USCI_I2C_enable(USCI_B1_BASE);
	//	SFRIE1 |= OFIE | NMIIE | ACCVIE | VMAIE;// | WDTIE;
}
void download_config (void);
RTN_TYPE Serial_transferFlashImageToRAM(void)
{
	int i; unsigned int size;
//	unsigned char *p;
//	configMem_updateFLASH((unsigned char*)&buff[8], sizeof(struct configMemRAM_t));
//	p = &buff[0];
//	for (i=0; i<buffSize;i++) 						{*p = 0x00; p++;}
//	p = (unsigned char *)pConfigMemRAM;
//	for (i=0; i<sizeof(struct configMemRAM_t);i++) 	{*p = 0x55; p++;}

	unsigned int offset = 0;

//	size = &pConfigMemRAM->sysConfigAddress.End - &pConfigMemRAM->sysConfigAddress.Start -1 ;
	size = sizeof(struct SysConfigAddress) - 2;
	configMem_copyToRAM((unsigned char*)&pConfigMemRAM->sysConfigAddress.Start+1, size, offset);
	offset += size; //pConfigMemRAM

//	size = &pConfigMemRAM->sysConfig.End - &pConfigMemRAM->sysConfig.Start -1;
	size = sizeof(struct SysConfig) - 2;
	configMem_copyToRAM((unsigned char*)&pConfigMemRAM->sysConfig.Start+1, size, offset);
	offset += size;

//	size = sizeof(struct SI) - 2;
	for (i=0; i<TOTAL_SI; i++) {
		size = &pConfigMemRAM->si[i].End - &pConfigMemRAM->si[i].Start - 1;
		configMem_copyToRAM((unsigned char*)&pConfigMemRAM->si[i].Start+1, size, offset);
		offset += size;//sizeof(struct SI) - 4;
//		size = &pConfigMemRAM->si[i].End2 - &pConfigMemRAM->si[i].Start2 - 1;
//		configMem_copyToRAM((unsigned char*)&pConfigMemRAM->si[i].Start2+1, size, offset);
//		offset += size;
	}
	size = sizeof(struct SO) - 2;
	for (i=0; i<(TOTAL_SZ); i++) {
		configMem_copyToRAM((unsigned char*)&pConfigMemRAM->sz[i].Start+1, size, offset);
		offset += size;//sizeof(struct SI) - 4;
	}
	for (i=0; i<(TOTAL_SO); i++) {
		configMem_copyToRAM((unsigned char*)&pConfigMemRAM->so[i].Start+1, size, offset);
		offset += size;//sizeof(struct SI) - 4;
	}
	for (i=0; i<(TOTAL_REL); i++) {
		configMem_copyToRAM((unsigned char*)&pConfigMemRAM->rl[i].Start+1, size, offset);
		offset += size;//sizeof(struct SI) - 4;
	}

	size = sizeof(struct GA) - 2;
	configMem_copyToRAM((unsigned char*)&pConfigMemRAM->ga.Start+1, size, offset);
	offset += size;

	size = sizeof(struct xAbort) - 2;
	configMem_copyToRAM((unsigned char*)&pConfigMemRAM->xabort.Start+1, size, offset);
	offset += size;

	size = sizeof(struct HWTests) - 2;
	configMem_copyToRAM((unsigned char*)&pConfigMemRAM->hwTests.Start+1, size, offset);
	offset += size;

	size = sizeof(struct FWS) - 2;
	for (i=0; i<(TOTAL_FWS_SECTORS); i++) {
		configMem_copyToRAM((unsigned char*)&pConfigMemRAM->fws[i].Start+1, size, offset);
		offset += size;//sizeof(struct SI) - 4;
	}

	size = sizeof(struct REVNO) - 2;
	configMem_copyToRAM((unsigned char*)&pConfigMemRAM->revno.Start+1, size, offset);
	offset += size;

	size = sizeof(struct FWS_Ext02) - 2;
	for (i=0; i<(TOTAL_FWS_SECTORS); i++) {
		configMem_copyToRAM((unsigned char*)&pConfigMemRAM->fwsEx02[i].Start+1, size, offset);
		offset += size;//sizeof(struct SI) - 4;
	}
	for (i=0; i<TOTAL_SI; i++) {
//		size = &pConfigMemRAM->si[i].End - &pConfigMemRAM->si[i].Start - 1;
//		configMem_copyToRAM((unsigned char*)&pConfigMemRAM->si[i].Start+1, size, offset);
//		offset += size;//sizeof(struct SI) - 4;
		size = &pConfigMemRAM->siGas[i].End - &pConfigMemRAM->siGas[i].Start - 1;
		configMem_copyToRAM((unsigned char*)&pConfigMemRAM->siGas[i].Start+1, size, offset);
		offset += size;
	}
	size = sizeof(struct DSP_Message) - 2;
	for (i=0; i<48; i++) {
		configMem_copyToRAM((unsigned char*)&pConfigMemRAM->dspMessage[i].Start+1, size, offset);
		offset += size;//sizeof(struct SI) - 4;
	}
	return RTN_SUCCESS;
}
void get_config_rev(void)
{
	unsigned char ack;// = receivedData;//0xff;//receivedData;
	receivedData = p_buffBegin[out];//buff[out];
	out = (out+1)%buffSize;
	if (out < in)
		processData = 1;
	if (turn == ST_INIT)
	{	I2C_SO_Off(OSO_MASK,SO_LED_MASK); Relay_AllOff(); I2CioDriver_reset();
		disableAllMaskableInterruptsButSerial();
		USCI_UART_transmitData(USCI_A0_BASE,receivedData);
		while (!USCI_UART_getInterruptStatus(USCI_A0_BASE, USCI_UART_TRANSMIT_INTERRUPT_FLAG)) ;
		ack = UPPER_REV_BYTE_COUNT;//byte[0];//0xff;//0x14;//0x1450;
		USCI_UART_transmitData(USCI_A0_BASE,ack);
		turn = ST_ACK_FROM_HI_BYTE;
	}
	else if (turn == ST_ACK_FROM_HI_BYTE)
	{
		while (!USCI_UART_getInterruptStatus(USCI_A0_BASE, USCI_UART_TRANSMIT_INTERRUPT_FLAG)) ;
		ack = LOWER_REV_BYTE_COUNT;//byte[1];//0xff;///0x53;//0x1450;
		USCI_UART_transmitData(USCI_A0_BASE,ack);
		turn = ST_ACK_FROM_LO_BYTE;
	}
	else if (turn == ST_ACK_FROM_LO_BYTE)
	{
		while (!USCI_UART_getInterruptStatus(USCI_A0_BASE, USCI_UART_TRANSMIT_INTERRUPT_FLAG)) ;
		ack = REV_BYTE;//byte[1];//0xff;///0x53;//0x1450;
		USCI_UART_transmitData(USCI_A0_BASE,ack);
		turn = ST_ACK_FROM_REV_BYTE;
	}
	else if (turn == ST_ACK_FROM_REV_BYTE)
	{ // rx 0x86
		while (!USCI_UART_getInterruptStatus(USCI_A0_BASE, USCI_UART_TRANSMIT_INTERRUPT_FLAG)) ;
//		receivedData = REV_BYTE;//byte[1];//0xff;///0x53;//0x1450;
//		USCI_UART_transmitData(USCI_A0_BASE,receivedData);
		turn = ST_RCV_CONFIG_1;
	}
	else if (turn == ST_RCV_CONFIG_1)
	{ // rx 0x02
		// if rx == 0x01, then download configuration
		if (receivedData == 0x01)
		{
			Serial_mode = MODE_RCV_CFG_FROM_PC;
			while (!USCI_UART_getInterruptStatus(USCI_A0_BASE, USCI_UART_TRANSMIT_INTERRUPT_FLAG)) ;
			USCI_UART_transmitData(USCI_A0_BASE,receivedData);
			__no_operation();
			while (!USCI_UART_getInterruptStatus(USCI_A0_BASE, USCI_UART_TRANSMIT_INTERRUPT_FLAG)) ;

		}
		// if rx == 0x02, then upload configuration
		else if (receivedData == 0x02)
		{
			Serial_mode = MODE_SEND_CFG_TO_PC;
			while (!USCI_UART_getInterruptStatus(USCI_A0_BASE, USCI_UART_TRANSMIT_INTERRUPT_FLAG)) ;
			USCI_UART_transmitData(USCI_A0_BASE,receivedData);
			__no_operation();
			while (!USCI_UART_getInterruptStatus(USCI_A0_BASE, USCI_UART_TRANSMIT_INTERRUPT_FLAG)) ;
			ack = 0x13;//byte[1];//0xff;///0x53;//0x1450;
			USCI_UART_transmitData(USCI_A0_BASE,ack);
		}
		turn = ST_RCV_CONFIG_2;
	}
}
void upload_config (void) // what I think is right
{
	unsigned char ack;// = receivedData;//0xff;//receivedData;
	receivedData = p_buffBegin[out];//buff[out];
	out = (out+1)%buffSize;
	if (out < in)
		processData = 1;
	if (turn == ST_RCV_CONFIG_2)
	{ // rx 0x13
		__delay_cycles(10000);
		while (!USCI_UART_getInterruptStatus(USCI_A0_BASE, USCI_UART_TRANSMIT_INTERRUPT_FLAG)) ;
		ack = 0xA6;//byte[1];//0xff;///0x53;//0x1450;
		USCI_UART_transmitData(USCI_A0_BASE,ack);
//		USCI_UART_transmitData(USCI_A0_BASE,receivedData);
		while (!USCI_UART_getInterruptStatus(USCI_A0_BASE, USCI_UART_TRANSMIT_INTERRUPT_FLAG)) ;
		__no_operation();
		turn = ST_RCV_CONFIG_3;
	}
	else if (turn == ST_RCV_CONFIG_3)
	{ // rx 0xA6 -- now start sending the configuration
		while (!USCI_UART_getInterruptStatus(USCI_A0_BASE, USCI_UART_TRANSMIT_INTERRUPT_FLAG)) ;
		lastByte = 0;
		byteIndex = 0;
		lastByte = configMem_getByte(byteIndex++);
		USCI_UART_transmitData(USCI_A0_BASE,lastByte);
		while (!USCI_UART_getInterruptStatus(USCI_A0_BASE, USCI_UART_TRANSMIT_INTERRUPT_FLAG)) ;
		__no_operation();
		count = 5029;
		turn = ST_RCV_CONFIG;
//		count = buff[out-2]<<8;
//		count += buff[out-1];
//		count = p_buffBegin[out-2]<<8;
//		count += p_buffBegin[out-1];

	}
	else if (turn == ST_RCV_CONFIG)
	{
		count--;
		while (!USCI_UART_getInterruptStatus(USCI_A0_BASE, USCI_UART_TRANSMIT_INTERRUPT_FLAG)) ;
		lastByte = configMem_getByte(byteIndex++);
		USCI_UART_transmitData(USCI_A0_BASE,lastByte);
//		USCI_UART_transmitData(USCI_A0_BASE,receivedData);
		if (count == 0)
		{
			__disable_interrupt();
			turn = ST_INIT;
			lastByte = 0;
			byteIndex = 0;
			while (!USCI_UART_getInterruptStatus(USCI_A0_BASE, USCI_UART_TRANSMIT_INTERRUPT_FLAG));
//			configMem_updateFLASH(&buff[8], 5029);
//			configMem_updateFLASH(&p_buffBegin[8], 5029);
//			configMem_copyToRAM((unsigned char*)&configMemRAM, sizeof(struct configMemRAM_t), 0);
//			configMem_copyToRAM((unsigned char*)p_buffBegin, sizeof(struct configMemRAM_t), 0);
		loadInhibitedSettings(pConfigMemRAM);
			if (RTN_SUCCESS != Serial_transferFlashImageToRAM())
			{
				__no_operation();
			}
			Serial_serialOperation = 0;
			Serial_mode = MODE_SEND_REV_TO_PC;
//			SI_24VOutput_CheckAndDrive();
//			SO_refreshConfigs();
//			SI_init();
//			SOSZ_init();
			in = out = 0;
			init_system();
//			I2CioDriver_init();
//			enableAllMaskableInterruptsButSerial();
//			Fault_init();
//			I2CioDriver_init();
//			LED_init();

		}
	}
}
void download_config (void) // what I think is right
{
//	unsigned char ack;// = receivedData;//0xff;//receivedData;
	receivedData = p_buffBegin[out];//buff[out];
	out = (out+1)%buffSize;
	if (out < in)
		processData = 1;
	if (turn == ST_RCV_CONFIG_2)
	{
		__delay_cycles(10000);
		while (!USCI_UART_getInterruptStatus(USCI_A0_BASE, USCI_UART_TRANSMIT_INTERRUPT_FLAG)) ;
		USCI_UART_transmitData(USCI_A0_BASE,receivedData);
		while (!USCI_UART_getInterruptStatus(USCI_A0_BASE, USCI_UART_TRANSMIT_INTERRUPT_FLAG)) ;
		__no_operation();
		turn = ST_RCV_CONFIG_3;
	}
	else if (turn == ST_RCV_CONFIG_3)
	{
		while (!USCI_UART_getInterruptStatus(USCI_A0_BASE, USCI_UART_TRANSMIT_INTERRUPT_FLAG)) ;
		USCI_UART_transmitData(USCI_A0_BASE,receivedData);
		while (!USCI_UART_getInterruptStatus(USCI_A0_BASE, USCI_UART_TRANSMIT_INTERRUPT_FLAG)) ;
		__no_operation();
		turn = ST_RCV_CONFIG;
//		count = buff[out-2]<<8;
//		count += buff[out-1];
		count = p_buffBegin[out-2]<<8;
		count += p_buffBegin[out-1];

	}
	else if (turn == ST_RCV_CONFIG)
	{
		count--;
		while (!USCI_UART_getInterruptStatus(USCI_A0_BASE, USCI_UART_TRANSMIT_INTERRUPT_FLAG)) ;
		USCI_UART_transmitData(USCI_A0_BASE,receivedData);
		if (out > 100)
			__no_operation();
		if (count == 0)
		{
			__disable_interrupt();
			turn = ST_INIT;
			while (!USCI_UART_getInterruptStatus(USCI_A0_BASE, USCI_UART_TRANSMIT_INTERRUPT_FLAG));
//			configMem_updateFLASH(&buff[8], 5029);
			configMem_updateFLASH(&p_buffBegin[8], 5029);
//			configMem_copyToRAM((unsigned char*)&configMemRAM, sizeof(struct configMemRAM_t), 0);
//			configMem_copyToRAM((unsigned char*)p_buffBegin, sizeof(struct configMemRAM_t), 0);
		loadInhibitedSettings(pConfigMemRAM);
			if (RTN_SUCCESS != Serial_transferFlashImageToRAM())
			{
				__no_operation();
			}
			Serial_serialOperation = 0;
			Serial_mode = MODE_SEND_REV_TO_PC;
//			SI_24VOutput_CheckAndDrive();
//			SO_refreshConfigs();
//			SI_init();
//			SOSZ_init();
			in = out = 0;
			init_system();
//			I2CioDriver_init();
//			enableAllMaskableInterruptsButSerial();
//			Fault_init();
//			I2CioDriver_init();
//			LED_init();



/*
			unsigned int offset = 0;
		    offset += sizeof(struct SysConfig) - 2;
		    offset += sizeof(struct SysConfigAddress) - 2; //pConfigMemRAM
		//    int i; unsigned int size; unsigned char *p = (unsigned char *)configMemRAM;
		    int i; unsigned int size; unsigned char *p = (unsigned char *)pConfigMemRAM;
		    for (i=0; i<sizeof(struct configMemRAM_t);i++) {*p = 0x00; p++;}
		    for (i=0; i<12; i++) {
		//    	size = &configMemRAM.si[i].End - &configMemRAM.si[i].Start;
		//    	configMem_copyToRAM((unsigned char*)&configMemRAM.si[i].Start+1, size, offset);
		       	size = &pConfigMemRAM->si[i].End - &pConfigMemRAM->si[i].Start;
		       	configMem_copyToRAM((unsigned char*)&pConfigMemRAM->si[i].Start+1, size, offset);
		    	offset += size-1;//sizeof(struct SI) - 4;

		    }*/
		}
	}
}
void Serial_thread(void)
{
//	while (1)
	    {
			if (processData)
			{
				processData = 0;
				switch (Serial_mode)
				{
					case MODE_SEND_REV_TO_PC:
						get_config_rev();
					break;
					case MODE_RCV_CFG_FROM_PC:
						download_config();
					break;
					case MODE_SEND_CFG_TO_PC:
						upload_config();
					break;
				}
			}
/*	    	if (turn == ST_RCV_CONFIG)
	    	{
	    		if (count <= 0)
	    		{
	    			unsigned char * src = &p_buffBegin[8];//&buff[8];
	    			unsigned char * dest = &stSysConfigAddress.Start + 1;
	    			__no_operation();
	    			*dest = *src;
	    			src++;

	    			dest = &stSysConfig.Start + 1;
	    			*dest = *src;
	    			src++;

	    			int i;
	    			for (i=0; i<TOTAL_SI; i++)
	    			{
	    				dest = &stSI[i].Start + 1;
	    				while (dest != &stSI[i].End)
	    				{
	    					*dest = *src;
							src++;
							dest++;
	    				}
	    			}
	    		}
	    		if (USCI_UART_queryStatusFlags(USCI_A0_BASE, USCI_UART_FRAMING_ERROR + USCI_UART_OVERRUN_ERROR + USCI_UART_PARITY_ERROR + USCI_UART_RECEIVE_ERROR))
				{
					__no_operation();
				}
	    	}*/
	    	__no_operation();
	    }
}
void Serial_sendCmdAndData(enum SerialCmd cmd, unsigned char * data, unsigned int numBytes)
{
	while (!USCI_UART_getInterruptStatus(USCI_A0_BASE, USCI_UART_TRANSMIT_INTERRUPT_FLAG)) ;
		USCI_UART_transmitData(USCI_A0_BASE,(unsigned char)cmd);

	while(numBytes--)
	{
		while (!USCI_UART_getInterruptStatus(USCI_A0_BASE, USCI_UART_TRANSMIT_INTERRUPT_FLAG)) ;
			USCI_UART_transmitData(USCI_A0_BASE,*data);
		data++;
	}
}
void Serial_sendCmdAndByte(enum SerialCmd cmd, unsigned char byte)
{
	while (!USCI_UART_getInterruptStatus(USCI_A0_BASE, USCI_UART_TRANSMIT_INTERRUPT_FLAG)) ;
		USCI_UART_transmitData(USCI_A0_BASE,(unsigned char)cmd);

	while (!USCI_UART_getInterruptStatus(USCI_A0_BASE, USCI_UART_TRANSMIT_INTERRUPT_FLAG)) ;
			USCI_UART_transmitData(USCI_A0_BASE,byte);
}
//******************************************************************************
//
//This is the USCI_A0 interrupt vector service routine.
//
//******************************************************************************
#pragma vector=USCI_A0_VECTOR
__interrupt void USCI_A0_ISR (void)
{
	WDT_A_resetTimer(WDT_A_BASE);
    switch (__even_in_range(UCA0IV,4)){
        //Vector 2 - RXIFG
        case 2:
        	Serial_serialOperation = 1;
            *(p_buffBegin+in) = USCI_UART_receiveData(USCI_A0_BASE);
            in = (in+1)%buffSize;
            processData = 1;
            break;
        default: break;
    }
}
